package com.azzcs.consumer.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

/**
 * @Author: wzg
 * @Date: 2021/2/6 下午12:09
 */
@Slf4j
public class TPMonitorDataCenter implements Runnable {
    private final static TPMonitorDataCenter INSTANCE =
            new TPMonitorDataCenter();

    private ReentrantLock lock = new ReentrantLock();

    private Map<String, List<Entry>> content =
            new ConcurrentHashMap<>();
    private ExecutorService executor= Executors
            .newFixedThreadPool(Runtime.getRuntime()
                    .availableProcessors());

    private TPMonitorDataCenter() {
        ScheduledExecutorService scheduled =
                Executors.newSingleThreadScheduledExecutor();
        scheduled.scheduleWithFixedDelay(this, 5,5, TimeUnit.SECONDS);
    }

    public static TPMonitorDataCenter getInstance(){
        return INSTANCE;
    }

    @Override
    public void run() {
        try {
            long aMinuteAgo = System.currentTimeMillis() - (1L * 60 * 1000);
            lock.lock();
            content.forEach((k,v)->{
                List<Entry> aMinuteData = v.stream()
                        .filter(entry -> entry.startTime > aMinuteAgo)
                        .sorted()
                        .collect(Collectors.toList());
                content.put(k, aMinuteData);
                if(!CollectionUtils.isEmpty(aMinuteData)) {
                    log.info("前一分钟,方法："+k+" 的 P90："+aMinuteData.get(aMinuteData.size() * 90 / 100).executeTime);
                    log.info("前一分钟方法："+k+" 的 P99："+aMinuteData.get(aMinuteData.size() * 99 / 100).executeTime);
                }

            });

        }finally {
            lock.unlock();
        }
    }

    public void add(String methodName, long startTime, long executeTime) {
        executor.execute(()->{
            Entry entry = new Entry(methodName,startTime,executeTime);
            content.putIfAbsent(entry.methodName,new ArrayList<>());
            List<Entry> entries = content.get(entry.methodName);
            try {
                lock.lock();
                entries.add(entry);
            }finally {
                lock.unlock();
            }
        });
    }

    private class Entry implements Comparable<Entry>{
        public String methodName;
        public Long startTime;
        public Long executeTime;

        public Entry(String methodName,Long startTime, Long executeTime) {
            this.methodName = methodName;
            this.executeTime = executeTime;
            this.startTime = startTime;
        }

        @Override
        public int compareTo(Entry entry) {
            return (int) (this.executeTime - entry.executeTime);
        }
    }
}
